package system

import (
	"admin_template/bff/admin/internal/entity/ent/sysapi"
	"context"
	"fmt"
	"reflect"
	"time"

	"admin_template/bff/admin/hertz_gen/base"
	genSysRole "admin_template/bff/admin/hertz_gen/system/sys_role"
	"admin_template/bff/admin/internal/base/data"
	"admin_template/bff/admin/internal/entity/ent"
	"admin_template/bff/admin/internal/entity/ent/predicate"
	"admin_template/bff/admin/internal/entity/ent/sysrole"
	"admin_template/bff/admin/internal/entity/ent/sysrule"
	"admin_template/bff/admin/internal/service/system"

	"gitee.com/byx_darwin/go-tools/kitex/rpc_error"
	toolsTime "gitee.com/byx_darwin/go-tools/tools/time"
	"github.com/pkg/errors"
)

type roleRepo struct {
	*data.Data
	db *ent.SysRoleClient
}

func NewRoleRepo(d *data.Data) system.RoleRepo {
	return &roleRepo{
		Data: d,
		db:   d.DB.SysRole,
	}
}

func (repo *roleRepo) Add(ctx context.Context,
	req any) error {
	info, ok := req.(*genSysRole.AddRequest)
	if !ok {
		return rpc_error.NewBizStatusError(rpc_error.ErrorTypeInvalid,
			errors.New("req not *genSysRole.AddRequest"))
	}
	err := repo.db.Create().
		SetName(info.Name).
		SetRemark(info.Remark).
		SetCode(info.Code).
		SetSort(int64(info.Sort)).Exec(ctx)
	if err != nil {
		msg := fmt.Sprintf("sys role create(name:%s)",
			info.Name)
		if ent.IsConstraintError(err) {
			err = errors.Wrap(err, msg)
			return rpc_error.NewBizStatusError(rpc_error.ErrorTypeDataRepeat, err)
		} else {
			err = errors.Wrap(err, msg)
			return rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlInsertFailure, err)
		}
	}
	return nil
}
func (repo *roleRepo) UpdateInfo(ctx context.Context,
	req any) error {
	info, ok := req.(*genSysRole.UpdateRequest)
	if !ok {
		return rpc_error.NewBizStatusError(rpc_error.ErrorTypeInvalid,
			errors.New("req not *genSysRole.UpdateRequest"))
	}
	err := repo.db.UpdateOneID(int(info.Id)).
		SetName(info.Name).
		SetRemark(info.Remark).
		SetCode(info.Code).
		SetSort(int64(info.Sort)).Exec(ctx)
	if err != nil {
		msg := fmt.Sprintf("sys role update(name:%s)",
			info.Name)
		if ent.IsConstraintError(err) {
			err = errors.Wrap(err, msg)
			return rpc_error.NewBizStatusError(rpc_error.ErrorTypeDataRepeat, err)
		} else {
			err = errors.Wrap(err, msg)
			return rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlInsertFailure, err)
		}
	}
	return nil
}

func (repo *roleRepo) List(ctx context.Context,
	req any) (int64, int64, int64, []any, error) {
	info, ok := req.(*genSysRole.ListRequest)
	if !ok {
		return 0, 0, 0, nil, rpc_error.NewBizStatusError(rpc_error.ErrorTypeInvalid,
			errors.New("req not *genSysRole.ListRequest"))
	}
	predicates := make([]predicate.SysRole, 0)
	switch info.State {
	case 1:
		predicates = append(predicates, sysrole.DeleteTimeNotNil())
	case 2:
		predicates = append(predicates, sysrole.DeleteTimeIsNil())
	}
	sql := repo.db.Query()
	if len(predicates) > 0 {
		sql = sql.Where(predicates...)
	}
	total, err := sql.Count(ctx)
	if err != nil {
		err = errors.Wrap(err,
			fmt.Sprintf("分页查询角色总数失败: "))
		return 0, info.Page, info.Size, nil, rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlQueryFailure, err)
	}
	if total == 0 {
		return 0, info.Page, info.Size, nil, nil
	}
	offset := (info.Page - 1) * info.Size
	list, err := sql.Select(repo.sqlSelect()...).
		Offset(int(offset)).
		Limit(int(info.Size)).Order(ent.Asc(sysrole.FieldSort)).All(ctx)
	if err != nil {
		err = errors.Wrap(err,
			fmt.Sprintf("分页查询角色列表失败: "))
		return 0, info.Page, info.Size, nil, rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlQueryFailure, err)
	}
	companyList := make([]any, 0, len(list))
	for _, value := range list {
		companyList = append(companyList, repo.genInfo(value))
	}
	return int64(total), info.Page, info.Size, companyList, nil
}

func (repo *roleRepo) All(ctx context.Context,
	req any) ([]any, error) {
	info, ok := req.(*genSysRole.AllRequest)
	if !ok {
		return nil, rpc_error.NewBizStatusError(rpc_error.ErrorTypeInvalid,
			errors.New("req not *genSysRole.AllRequest"))
	}
	predicates := make([]predicate.SysRole, 0)
	switch info.State {
	case 1:
		predicates = append(predicates, sysrole.DeleteTimeNotNil())
	case 2:
		predicates = append(predicates, sysrole.DeleteTimeIsNil())
	}
	sql := repo.db.Query()
	if len(predicates) > 0 {
		sql = sql.Where(predicates...)
	}
	list, err := sql.Select(repo.sqlSelect()...).Order(ent.Asc(sysrole.FieldSort)).All(ctx)
	if err != nil {
		err = errors.Wrap(err,
			fmt.Sprintf("分页查询角色列表失败: "))
		return nil, rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlQueryFailure, err)
	}
	companyList := make([]any, 0, len(list))
	for _, value := range list {
		companyList = append(companyList, repo.genInfo(value))
	}
	return companyList, nil
}
func (repo *roleRepo) One(ctx context.Context,
	id int) (any, error) {
	info, err := repo.db.Query().Where(sysrole.ID(id)).
		Select(repo.sqlSelect()...).First(ctx)
	if err != nil {
		if ent.IsNotFound(err) {
			return nil, rpc_error.NewBizStatusError(rpc_error.ErrorTypeDataInvalid, err)
		}
		return nil, rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlQueryFailure, err)
	}
	return repo.genInfo(info), nil
}

func (repo *roleRepo) StateUpdate(ctx context.Context,
	info *base.StateUpdateRequest) error {
	switch info.State {
	case 0:
		err := repo.db.UpdateOneID(int(info.ID)).SetDeleteTime(time.Now()).Exec(ctx)
		if err != nil {
			err = errors.Wrap(err,
				fmt.Sprintf("更新角色状态失败(ID:%d): ", info.ID))
			return rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlUpdateFailure, err)
		}
	case 1:
		err := repo.db.UpdateOneID(int(info.ID)).ClearDeleteTime().Exec(ctx)
		if err != nil {
			err = errors.Wrap(err,
				fmt.Sprintf("更新角色状态失败(ID:%d): ", info.ID))
			return rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlUpdateFailure, err)
		}
	case 2:
		err := repo.db.DeleteOneID(int(info.ID)).Exec(ctx)
		if err != nil {
			err = errors.Wrap(err,
				fmt.Sprintf("删除公司失败(ID:%d): ", info.ID))
			return rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlDeleteFailure, err)
		}
	}
	return nil
}

func (repo *roleRepo) UpdateRoleRule(ctx context.Context,
	info *genSysRole.UpdateRuleRequest) error {
	menuID := make([]int, 0, len(info.MenuIds))
	for _, item := range info.MenuIds {
		menuID = append(menuID, int(item))
	}
	err := repo.db.UpdateOneID(int(info.Id)).AddRuleIDs(menuID...).Exec(ctx)
	if err != nil {
		err = errors.Wrap(err,
			fmt.Sprintf("更新角色菜单失败(ID:%d): ", info.Id))
		return rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlUpdateFailure, err)
	}
	return nil
}

func (repo *roleRepo) UpdateRoleApi(ctx context.Context,
	info *genSysRole.UpdateApiRequest) (bool, error) {
	tmpInfo, err := repo.db.Query().
		WithApis(func(query *ent.SysApiQuery) {
			query.Select(sysapi.FieldID)
		}).
		Where(sysrole.IDEQ(int(info.Id))).Only(ctx)
	if err != nil {
		if ent.IsNotFound(err) {
			err = errors.Wrap(err,
				fmt.Sprintf("无效角色(ID:%d): ", info.Id))
			return false, rpc_error.NewBizStatusError(rpc_error.ErrorTypeDataInvalid, err)
		} else {
			err = errors.Wrap(err,
				fmt.Sprintf("查询角色信息失败(ID:%d): ", info.Id))
			return false, rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlQueryFailure, err)
		}
	}
	isUpdate := false
	tmpApIds := make([]int64, 0, len(tmpInfo.Edges.Apis))
	for _, item := range tmpInfo.Edges.Apis {
		tmpApIds = append(tmpApIds, int64(item.ID))
	}
	apiIDs := make([]int, 0, len(tmpInfo.Edges.Apis))
	for _, item := range info.ApiIds {
		apiIDs = append(apiIDs, int(item))
	}
	if !reflect.DeepEqual(info.ApiIds, tmpApIds) {
		isUpdate = true
		err := repo.db.UpdateOneID(int(info.Id)).AddAPIIDs(apiIDs...).Exec(ctx)
		if err != nil {
			err = errors.Wrap(err,
				fmt.Sprintf("更新角色API失败(ID:%d): ", info.Id))
			return false, rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlUpdateFailure, err)
		}
	}
	return isUpdate, nil

}

func (repo *roleRepo) sqlSelect() []string {
	return []string{
		sysrole.FieldID,
		sysrole.FieldName,
		sysrole.FieldRemark,
		sysrole.FieldSort,
		sysrole.FieldDeleteTime,
		sysrole.FieldCreateTime,
		sysrole.FieldUpdateTime,
		sysrole.FieldCode,
	}
}
func (repo *roleRepo) genInfo(info *ent.SysRole) *genSysRole.Info {
	state := int32(1)
	if info.DeleteTime.Unix() > 0 {
		state = 0
	}
	return &genSysRole.Info{
		Id:         int64(info.ID),
		Name:       info.Name,
		Remark:     info.Remark,
		Sort:       int32(info.Sort),
		State:      state,
		Code:       info.Code,
		CreateTime: toolsTime.Format(info.CreateTime.Unix(), "YYYY-MM-DD HH:mm:ss", ""),
		UpdateTime: toolsTime.Format(info.UpdateTime.Unix(), "YYYY-MM-DD HH:mm:ss", ""),
	}
}

func (repo *roleRepo) Init(ctx context.Context) error {
	info := genSysRole.AddRequest{
		Name:   "超级管理员",
		Remark: "系统最高权限管理云",
		Sort:   1,
		Code:   "superAdmin",
	}
	apiList, err := repo.DB.SysApi.
		Query().Where(sysapi.DeleteTimeIsNil()).All(ctx)
	if err != nil {
		return err
	}
	apiIds := make([]int, 0, len(apiList))
	for _, value := range apiList {
		apiIds = append(apiIds, value.ID)
	}
	ruleList, err := repo.DB.SysRule.
		Query().Where(sysrule.DeleteTimeIsNil()).All(ctx)
	if err != nil {
		return err
	}
	ruleIds := make([]int, 0, len(ruleList))
	for _, value := range ruleList {
		ruleIds = append(ruleIds, int(value.ID))
	}
	err = repo.db.Create().
		SetName(info.Name).
		SetRemark(info.Remark).
		SetCode(info.Code).
		SetSort(int64(info.Sort)).
		AddRuleIDs(ruleIds...).
		AddAPIIDs(apiIds...).
		Exec(ctx)
	return err
}
